package internal.inference;

import inference.IType.IReturnTypeVariable;
import inference.IType.ITypeEquation;
import inference.IType.ITypeEquationVisitor;
import inference.IType.ITypeVariable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import parser.IParse.IArithmeticExpression;
import parser.IParse.IAssignment;
import parser.IParse.IBooleanExpression;
import parser.IParse.ICompoundStatement;
import parser.IParse.IDecimalLiteral;
import parser.IParse.IExpression;
import parser.IParse.IFunction;
import parser.IParse.IIfThenElse;
import parser.IParse.IIntegerLiteral;
import parser.IParse.IParseNode;
import parser.IParse.IReturn;
import parser.IParse.ITerm;
import parser.IParse.IVariable;
import parser.IParse.IWhile;
import parser.ParseNodeVisitor;

class TypeEquationVisitor extends ParseNodeVisitor implements ITypeEquationVisitor {

	private final List<ITypeEquation> equations;
	private final TypeFactory typeFactory;
	private final Map<ITypeVariable, IParseNode> nodeToVar;

	TypeEquationVisitor() {

		this.equations = new ArrayList<ITypeEquation>();
		this.typeFactory = new TypeFactory();
		this.nodeToVar = new HashMap<ITypeVariable, IParseNode>();
	}

	@Override
	public boolean visit(final IArithmeticExpression node) {

		final ITypeVariable lhs = typeFactory.newTypeVariable(node);
		final ITypeVariable exp1 = typeFactory.newTypeVariable(node.leftOperand());
		final ITypeVariable exp2 = typeFactory.newTypeVariable(node.rightOperand());
		nodeToVar.put(lhs, node);
		nodeToVar.put(exp1, node.leftOperand());
		nodeToVar.put(exp2, node.rightOperand());

		final ITypeEquation eq = typeFactory.newTypeEquation(lhs, exp1, node.operator().getText(), exp2);
		
		// Print eqn
		System.out.println(eq.toString());
		
		equations.add(eq);
		return true;
	}

	@Override
	public List<ITypeEquation> getEquations() {
		return equations;
	}

	@Override
	public Map<ITypeVariable, IParseNode> getVariableMap() {
		return nodeToVar;
	}

	public boolean visit(final IAssignment node){

		final ITypeVariable lhs = typeFactory.newTypeVariable(node.variable());
		final ITypeVariable exp =typeFactory.newTypeVariable(node.expression()); 
		nodeToVar.put(lhs, node);
		nodeToVar.put(exp, node.expression());
		final ITypeEquation equat = typeFactory.newTypeEquation(lhs, exp);
		System.out.println(equat.toString());
		equations.add(equat);
		return true;
	}
	
	public boolean visit(IBooleanExpression node){
		final ITypeVariable lhs = typeFactory.newTypeVariable(node);
		final ITypeVariable exp1 = typeFactory.newTypeVariable(node.leftOperand());
		final ITypeVariable exp2 = typeFactory.newTypeVariable(node.rightOperand());
		nodeToVar.put(lhs, node);
		nodeToVar.put(exp1, node.leftOperand());
		nodeToVar.put(exp2, node.rightOperand());
		final ITypeEquation eq = typeFactory.newTypeEquation(lhs, exp1, node.operator().getText(), exp2);
		System.out.println(eq.toString());
		equations.add(eq);
		return true;
	}
	public boolean visit(ITerm node){
		final ITypeVariable lhs = typeFactory.newTypeVariable(node);
		final ITypeVariable exp1 = typeFactory.newTypeVariable(node.leftOperand());
		final ITypeVariable exp2 = typeFactory.newTypeVariable(node.rightOperand());
		nodeToVar.put(lhs, node);
		nodeToVar.put(exp1, node.leftOperand());
		nodeToVar.put(exp2, node.rightOperand());
		final ITypeEquation eq = typeFactory.newTypeEquation(lhs, exp1, node.operator().getText(), exp2);
		System.out.println(eq.toString());
		equations.add(eq);
		return true;
	}
	public boolean visit(IReturn node){
		final ITypeVariable lhs =  typeFactory.newReturnVariable();
		final ITypeVariable exp = typeFactory.newTypeVariable(node.expression());
		nodeToVar.put(lhs, node);
		nodeToVar.put(exp, node.expression());
		final ITypeEquation eq = typeFactory.newTypeEquation(lhs,exp);
		System.out.println(eq.toString());
		equations.add(eq);
		return true;	
	}
	public boolean visit(IFunction node){
		return true;
	}
	public boolean visit(ICompoundStatement node){
		return true;
	}
	public boolean visit(IWhile node){
		return true;
	}
	public boolean visit(IIfThenElse node){
		return true;
	}
	public boolean visit(IVariable node){
		return true;
	}
	public boolean visit(IIntegerLiteral node){
		return true;
	}
	public boolean visit(IDecimalLiteral node){
		return true;
	}
	// similarly add code for other cases where a type equation is to be generated

}